#include "shell_cmd_list.h"
#include "srt/srt.h"
#include "ver.h"
#include "data_center/data_center.h"
#include "stdio.h"
#include "string.h"
#include "uart_interface.h"

#define LOG_TAG "SHELL"
#include "log/log.h"

#include "pico/time.h"

typedef void (*TShellCmdFunc)(Int32 argc, const Int8 argv[5][20]);

/**
 * TShellCmd shell指令结构体
 */
struct TShellCmdTag
{
    const Int8   *cmd;   ///< 指令名
    TShellCmdFunc func;  ///< 执行函数
    const Int8   *desc;  ///< 描述
};
typedef struct TShellCmdTag TShellCmd;

static Int8 argv_[5][20] = {0};
static void ShellCmdHelp(Int32 argc, const Int8 argv[5][20]);
static void ShellCmdLs(Int32 argc, const Int8 argv[5][20]);
static void ShellSet(Int32 argc, const Int8 argv[5][20]);
static void ShellGet(Int32 argc, const Int8 argv[5][20]);
static void ShellLog(Int32 argc, const Int8 argv[5][20]);
static void ShellVersion(Int32 argc, const Int8 argv[5][20]);

static const TShellCmd shell_cmd_list_[] = {
    {"help"   , ShellCmdHelp, "help    | help you to use this device\r\n"},
    {"ls"     , ShellCmdLs  , "ls      | list all command, and their desc\r\n"},
    {"set"    , ShellSet    , "set     | control device to change or switchover\r\n"
                              "        | Format: set [device] [operation]\r\n"
                              "        | Params:\r\n"
                              "        |   [device] \"acc\" \"pwr\"\r\n"
                              "        |   [operation] \"open\" \"close\"\r\n"},
    {"get"    , ShellGet    , "get     | get device status\r\n"
                              "        | Format: set [device]\r\n"
                              "        | Params:\r\n"
                              "        |   [device] \"acc\" \"pwr\" \"volt\" \"curr\"\r\n"},
    {"log"    , ShellLog    , "log     | ctrl log enable\r\n"
                              "        | Format: log [operation]\r\n"
                              "        | Params:\r\n"
                              "        |   [operation] \"open\" \"close\"\r\n"},
    {"version", ShellVersion, "version | output software version info\r\n"
                              "        | Format: version\r\n"},
};
static const Int32 shell_cmd_cnt_ = sizeof(shell_cmd_list_) / sizeof(TShellCmd);

static void UartSendStr(const Int8 *str)
{
    TUartSendData((UInt8 *)str, strlen((char *)str));
}

static void ShellCmdHelp(Int32 argc, const Int8 argv[5][20])
{
    UartSendStr("input \"ls\" to get all command desc!\r\n");
}

static void ShellCmdLs(Int32 argc, const Int8 argv[5][20])
{
    for (Int32 idx = 0; idx < shell_cmd_cnt_; idx ++)
    {
        UartSendStr("------------------------------------------------\r\n");
        UartSendStr(shell_cmd_list_[idx].desc);
    }
}

static void SendMsg2Ctrl(TDataCenterMsgType msg_type)
{
    TDataCenterMsg *new_msg = TDataCenterCreateMsg(0);

    if (new_msg != NULL)
    {
        new_msg->receiver    = kDataCenterUserCtrl;
        new_msg->msg_type    = msg_type;
        new_msg->content_len = 0;
        TDataCenterSendMsg(kDataCenterUserShell, new_msg);
    }
    else
    {
        LOG_E("%s malloc %d failed!", __FUNCTION__, sizeof(TDataCenterMsg));
    }
}

static void ShellSet(Int32 argc, const Int8 argv[5][20])
{
    if (argc < 3)
    {
        UartSendStr("param too few, please input \"set [device] [operation]\"!\r\n");
    }
    else if (strcmp(argv[1], "acc") == 0)
    {
        if (strcmp(argv[2], "open") == 0)
        {
            UartSendStr("get command: open acc\r\n");
            SendMsg2Ctrl(kDataCenterMsgTypeCmdOpenAcc);
        }
        else if (strcmp(argv[2], "close") == 0)
        {
            UartSendStr("get command: close acc\r\n");
            SendMsg2Ctrl(kDataCenterMsgTypeCmdCloseAcc);
        }
        else
        {
            UartSendStr("Unknown operation params\r\n");
        }
    }
    else if (strcmp(argv[1], "pwr") == 0)
    {
        if (strcmp(argv[2], "open") == 0)
        {
            UartSendStr("get command: open pwr\r\n");
            SendMsg2Ctrl(kDataCenterMsgTypeCmdOpenPwr);
        }
        else if (strcmp(argv[2], "close") == 0)
        {
            UartSendStr("get command: close pwr\r\n");
            SendMsg2Ctrl(kDataCenterMsgTypeCmdClosePwr);
        }
        else
        {
            UartSendStr("Unknown operation params\r\n");
        }
    }
    else
    {
        UartSendStr("Unknown device params\r\n");
    }
}

static void ShellGet(Int32 argc, const Int8 argv[5][20])
{
    if (argc < 2)
    {
        UartSendStr("param too few, please input \"get [device]\"!\r\n");
    }
    else if (strcmp(argv[1], "acc") == 0)
    {
        Bool acc_status = False;

        TSrtGetBool(kSrtBoolAccStatus, &acc_status);

        if (acc_status)
            UartSendStr("ACC ON\r\n");
        else
            UartSendStr("ACC OFF\r\n");
    }
    else if (strcmp(argv[1], "pwr") == 0)
    {
        Bool pwr_status = False;

        TSrtGetBool(kSrtBoolPwrStatus, &pwr_status);

        if (pwr_status)
            UartSendStr("PWR ON\r\n");
        else
            UartSendStr("PWR OFF\r\n");
    }
    else if (strcmp(argv[1], "volt") == 0)
    {
        Float volt = 0;

        if (TSrtGetFloat(kSrtFloatBusVolt, &volt))
        {
            Int8 send_buf[16] = {0};
            Int32 send_len = 0;

            send_len = snprintf(send_buf, sizeof(send_buf), "%f V\r\n", volt);
            if (send_len == sizeof(send_buf))
                send_buf[send_len - 1] = '\0';

            UartSendStr(send_buf);
        }
        else
            UartSendStr("NO VOLT DATA\r\n");
    }
    else if (strcmp(argv[1], "curr") == 0)
    {
        Float curr = 0;

        if (TSrtGetFloat(kSrtFloatBusCurr, &curr))
        {
            Int8 send_buf[16] = {0};
            Int32 send_len = 0;

            send_len = snprintf(send_buf, sizeof(send_buf), "%f mA\r\n", curr);
            if (send_len == sizeof(send_buf))
                send_buf[send_len - 1] = '\0';

            UartSendStr(send_buf);
        }
        else
            UartSendStr("NO CURR DATA\r\n");
    }
    else
    {
        UartSendStr("Unknown device params\r\n");
    }
}

static void ShellLog(Int32 argc, const Int8 argv[5][20])
{
    if (argc < 2)
    {
        UartSendStr("param too few, please input \"get [device]\"!\r\n");
    }
    else if (strcmp(argv[1], "open") == 0)
    {
        if (TSrtSetBool(kSrtBoolLogEnable, True))
            UartSendStr("LOG OPEN SUCC\r\n");
        else
            UartSendStr("LOG OPEN FAIL\r\n");
    }
    else if (strcmp(argv[1], "close") == 0)
    {
        if (TSrtSetBool(kSrtBoolLogEnable, False))
            UartSendStr("LOG CLOSE SUCC\r\n");
        else
            UartSendStr("LOG CLOSE FAIL\r\n");
    }
    else
    {
        UartSendStr("Unknown operation params\r\n");
    }
}

static void ShellVersion(Int32 argc, const Int8 argv[5][20])
{
    Int8 send_buf[36] = {0};
    Int32 send_len = 0;

    send_len = snprintf(send_buf, sizeof(send_buf), "software version: %d.%d\r\n", TGetMajorVersion(), TGetMinorVersion());
    if (send_len == sizeof(send_buf))
        send_buf[send_len - 1] = '\0';
    UartSendStr(send_buf);
}

void TShellGetData(UInt8 *data, UInt16 data_len)
{
    Int32 argc = 0;
    Int32 arg_len = 0;
    char *start = (char *)data;
    char *end   = (char *)data + data_len - 1;
    char *space = NULL;

    // LOG_HEX(kLogLevelTrace, "Shell Get Data", data, data_len);

    while (start < end && argc < 5)
    {
        space = strchr(start, ' ');

        if (space != NULL)
        {
            arg_len = space - start;

            if (arg_len > 0)
            {
                if (arg_len >= sizeof(argv_[argc]))
                {
                    LOG_W("argv[%d] too long, cut it. %d->%d", argc, arg_len, sizeof(argv_[argc]) - 1);
                    arg_len = sizeof(argv_[argc]) - 1;
                }

                memcpy(argv_[argc], start, arg_len);
                argv_[argc][arg_len] = '\0';
                LOG_I("argv[%d] len:%d content:%s", argc, arg_len, argv_[argc]);

                argc ++;
            }
            start = space + 1;
        }
        else
        {
            break;
        }
    };

    if (start < end && argc < 5)  ///< 采集最后一个参数
    {
        arg_len = end - start;

        if (arg_len >= sizeof(argv_[argc]))
        {
            LOG_W("argv[%d] too long, cut it. %d->%d", argc, arg_len, sizeof(argv_[argc]) - 1);
            arg_len = sizeof(argv_[argc]) - 1;
        }

        memcpy(argv_[argc], start, arg_len);
        argv_[argc][arg_len] = '\0';
        LOG_I("argv[%d] len:%d content:%s", argc, arg_len, argv_[argc]);
        argc ++;
    }

    LOG_I("argc=%d", argc);

    if (argc > 0)
    {
        Int32 idx = 0;

        for (; idx < shell_cmd_cnt_; idx ++)
        {
            if (strcmp(shell_cmd_list_[idx].cmd, argv_[0]) == 0)
            {
                shell_cmd_list_[idx].func(argc, argv_);
                break;
            }
        }

        if (idx == shell_cmd_cnt_)
        {
            UartSendStr("Unknown cmd\r\n");
        }
    }
    else
    {
        ShellCmdHelp(0, argv_);
    }
}
